#include "lines.hpp"
#include "types.h"
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>

namespace Trade {

static const price_t the_spliter = 11.99999;

void
Lines::print(const int64_t idx, const price_t hp_now) const noexcept
{
  auto ed = ext_rgn.ext_dir(hp_now);
  printf("\033[38;5;12m-- L --\033[0m eD%d ", ed);
  printf("U%d %.02lf %.02lf (%.02lf %.02lf) | D%d %.02lf %.02lf (%.02lf "
         "%.02lf) Ext %.02lf, %.02lf\n",
         nup,
         (hp_now - hp_up_2),
         (hp_now - hp_up_1),
         hp_up_2,
         hp_up_1,
         ndn,
         (hp_now - hp_dn_1),
         (hp_now - hp_dn_2),
         hp_dn_1,
         hp_dn_2,
         ext_rgn.hp_A,
         ext_rgn.hp_V);
}

bool
Lines::good_main_space() const noexcept
{
  return (hp_up_1 - hp_dn_1 > MINIMAL_GOOD_SPACE);
}

bool
Lines::between_the_lines(const price_t hpx, const dir_t d) noexcept
{
  bool ret = false;
  if (d > 0) {
    if (nup > 1)
      ret = (hpx < hp_up_2);
    else if (nup > 0)
      ret = (hpx < hp_up_1);
  } else {
    if (ndn > 1)
      ret = (hpx > hp_dn_2);
    else if (nup > 0)
      ret = (hpx > hp_dn_1);
  }
  return ret;
}

price_t
Lines::get_max_min_line(const dir_t d) noexcept
{
  price_t l = 0;
  if (d > 0) {
    if (nup > 1)
      l = hp_up_2;
    else if (nup > 0)
      l = hp_up_1;
  } else {
    if (ndn > 1)
      l = hp_dn_2;
    else if (ndn > 0)
      l = hp_dn_1;
  }
  return l;
}

price_t
Lines::get_middle_line(const dir_t d) noexcept
{
  price_t l = 0;
  if (d > 0) {
    if (nup > 0) {
      l = hp_up_1;
      if ((hpx - l) > 0) {
        if (nup > 1) {
          l = hp_up_2;
        }
      }
    }
  } else {
    if (ndn > 0) {
      l = hp_dn_1;
      if ((hpx - l) < 0) {
        if (ndn > 1) {
          l = hp_dn_2;
        }
      }
    }
  }
  return l;
}

price_t
Lines::get_line1_space(const dir_t d, const price_t x) const noexcept
{
  price_t space = 0;

  if (d > 0) {
    if (x <= hp_up_1)
      space = hp_up_1 - x;
    else if (x <= hp_up_2)
      space = hp_up_2 - x;
    else
      space = 20;
  } else if (d < 0) {
    if (x >= hp_dn_1)
      space = x - hp_dn_1;
    else if (x >= hp_dn_2)
      space = x - hp_dn_2;
    else
      space = 20;
  }

  return space;
}

reached_t
LinesGodenPnt::reached_only(const price_t hpx) noexcept
{
  reached_t r = LIMIT_REACHED::REACHED_NONE;
  if ((ndn > 1) && (hpx + 2 >= gp_dn2)) {
    r |= (LIMIT_REACHED::REACHED_UP_2);
  }
  if ((ndn > 0) && (hpx + 2 >= gp_dn1)) {
    r |= LIMIT_REACHED::REACHED_UP_1;
  }

  if ((nup > 1) && (hpx - 2 <= gp_up2)) {
    r |= (LIMIT_REACHED::REACHED_DN_2);
  }
  if ((nup > 0) && (hpx - 2 <= gp_up1)) {
    r |= LIMIT_REACHED::REACHED_DN_1;
  }
  reached |= r;
  return r;
}

reached_t
Lines::reached_only(const price_t hpx) const noexcept
{
  reached_t r = LIMIT_REACHED::REACHED_NONE;
  if ((nup > 1) && (hpx + 2 >= hp_up_2)) {
    r |= (LIMIT_REACHED::REACHED_UP_2 | LIMIT_REACHED::REACHED_UP_1);
  } else if ((nup > 0) && (hpx + 2 >= hp_up_1)) {
    r |= LIMIT_REACHED::REACHED_UP_1;
  }

  if ((ndn > 1) && (hpx - 2 <= hp_dn_2)) {
    r |= (LIMIT_REACHED::REACHED_DN_2 | LIMIT_REACHED::REACHED_DN_1);
  } else if ((ndn > 0) && (hpx - 2 <= hp_dn_1)) {
    r |= LIMIT_REACHED::REACHED_DN_1;
  }

  return r;
}

reached_t
Lines::limit_reached(const price_t hp_now) noexcept
{
  reached_t r = 0;
  if ((!(reached & LIMIT_REACHED::REACHED_UP_2)) && (nup > 1) &&
      (hp_now + 2 - hp_up_2 >= 0)) {
    r |= (LIMIT_REACHED::REACHED_UP_2 | LIMIT_REACHED::REACHED_UP_1);
  } else if ((!(reached & LIMIT_REACHED::REACHED_UP_1)) && (nup > 0) &&
             (hp_now + 2 - hp_up_1 >= 0)) {
    r |= LIMIT_REACHED::REACHED_UP_1;
  }

  if ((!(reached & LIMIT_REACHED::REACHED_DN_2)) && (ndn > 1) &&
      (hp_now - 2 - hp_dn_2 <= 0)) {
    r |= (LIMIT_REACHED::REACHED_DN_2 | LIMIT_REACHED::REACHED_DN_1);
  } else if ((!(reached & LIMIT_REACHED::REACHED_DN_1)) && (ndn > 0) &&
             (hp_now - 2 - hp_dn_1 <= 0)) {
    r |= LIMIT_REACHED::REACHED_DN_1;
  }

  if (nup > 0) {
    auto delta = std::abs(hp_now - hp_up_1);
    if (delta < nd.up_1) {
      nd.up_1 = delta;
      auto pct = delta / (hp_up_1 - hp_dn_1);
      if (pct < nd.up_1_pct)
        nd.up_1_pct = pct;
    }
    if (nup > 1) {
      delta = std::abs(hp_now - hp_up_2);
      if (delta < nd.up_2) {
        nd.up_2 = delta;
        auto pct = delta / (hp_up_2 - hp_dn_1);
        if (pct < nd.up_2_pct)
          nd.up_2_pct = pct;
      }
    }
  }

  if (ndn > 0) {
    auto delta = std::abs(hp_now - hp_dn_1);
    if (delta < nd.dn_1) {
      nd.dn_1 = delta;
      auto pct = delta / (hp_up_1 - hp_dn_1);
      if (pct < nd.dn_1_pct)
        nd.dn_1_pct = pct;
    }
    if (ndn > 1) {
      delta = std::abs(hp_now - hp_dn_2);
      if (delta < nd.dn_2) {
        nd.dn_2 = delta;
        auto pct = delta / (hp_up_1 - hp_dn_2);
        if (pct < nd.dn_2_pct)
          nd.dn_2_pct = pct;
      }
    }
  }

  if (reached != (reached | r)) {
    reached |= r;
    return r;
  }
  return (0);
}

price_t
Lines::get_opposite_line_1(const dir_t d, const price_t hpx) const noexcept
{
  price_t l = 0;
  if (d > 0) {
    // return down-line
    if (ndn > 1) {
      if (hpx > hp_dn_1)
        l = hp_dn_1;
      else if (hpx > hp_dn_2)
        l = hp_dn_2;
      else
        l = hpx;
    } else if (ndn > 0) {
      if (hpx > hp_dn_1)
        l = hp_dn_1;
      else
        l = hpx;
    } else {
      l = hpx;
    }
  } else {
    // return up-line
    if (nup > 1) {
      if (hpx < hp_up_1)
        l = hp_up_1;
      else if (hpx < hp_up_2)
        l = hp_up_2;
      else
        l = hpx;
    } else if (ndn > 0) {
      if (hpx < hp_up_1)
        l = hp_up_1;
      else
        l = hpx;
    } else {
      l = hpx;
    }
  }
  return l;
}

bool
Lines::good_space_for_dir(const price_t x,
                          const dir_t d,
                          int8_t& n_target,
                          int8_t& x1_id,
                          int8_t& x2_id,
                          price_t& space,
                          price_t& target_x1,
                          price_t& target_x2) noexcept
{
  space = 0;
  if (d > 0 && nup > 0) {
    n_target = 1;
    if (x <= hp_up_1) {
      space = hp_up_1 - x;
      target_x1 = hp_up_1;
      x1_id = 1;
      if (nup > 1) {
        target_x2 = hp_up_2;
        n_target = 2;
        x2_id = 2;
      }
    } else if (x <= hp_up_2) {
      space = hp_up_2 - x;
      target_x1 = hp_up_2;
      x1_id = 2;
    } else {
      space = 20;
      target_x1 = hpx + 20;
      x1_id = 3;
    }
  } else if (d < 0 && ndn > 0) {
    n_target = 1;
    if (x >= hp_dn_1) {
      space = x - hp_dn_1;
      target_x1 = hp_dn_1;
      x1_id = 1;
      if (ndn > 1) {
        target_x2 = hp_dn_2;
        n_target = 2;
        x2_id = 2;
      }
    } else if (x >= hp_dn_2) {
      space = x - hp_dn_2;
      target_x1 = hp_dn_2;
      x1_id = 2;
    } else {
      space = 20;
      target_x1 = hpx - 20;
      x1_id = 3;
    }
  }

  return ((space > 14.999) && (main_space() > space));
}

bool
Lines::operator!=(const Lines& l) const noexcept
{
  bool yes = false;
  if (l.ndn != ndn || l.nup != nup) {
    yes = true;
  } else {
    if (ndn > 1) {
      yes = std::abs(hp_dn_2 - l.hp_dn_2) > MINIMAL_LINE_CHANGE;
    } else {
      yes = std::abs(hp_dn_1 - l.hp_dn_1) > MINIMAL_LINE_CHANGE;
    }

    if (!yes) {
      if (nup > 1) {
        yes = std::abs(hp_up_2 - l.hp_up_2) > MINIMAL_LINE_CHANGE;
      } else {
        yes = std::abs(hp_up_1 - l.hp_up_1) > MINIMAL_LINE_CHANGE;
      }
    }
  }

  return yes;
}

void
Lines::reset() noexcept
{
  memset((void*)this, 0, sizeof(Lines));
}

price_t
Lines::main_space() const noexcept
{
  return (hp_up_1 - hp_dn_1);
}

int8_t
Lines::amount(const dir_t d) noexcept
{
  return (d > 0) ? nup : ((d < 0) ? ndn : 0);
}

void
Lines::copy(const Lines& l) noexcept
{
  memcpy((void*)(this), &l, sizeof(Lines));
  reached = 0;
}

void
Lines::copy(const Lines* l) noexcept
{
  memcpy((void*)(this), l, sizeof(Lines));
  reached = 0;
}

bool
Lines::is_ext_top_btm(const price_t y, const dir_t d) noexcept
{
  return (
    (d > 0 && ext_rgn.top && (y - ext_rgn.hpx + ext_rgn.hp_A > -the_spliter)) ||
    (d < 0 && ext_rgn.btm && (y - ext_rgn.hpx + ext_rgn.hp_V < the_spliter)));
}

dir_t
ExtRegion::large_reflection(const price_t hp) const noexcept
{
  bool yes =
    ((btm && hpx - hp_V > the_spliter) || (top && hp_A - hpx > the_spliter));
  if (yes)
    return (top) ? (-1) : (1);
  return (0);
}

dir_t
ExtRegion::ext_dir(const price_t y) const noexcept
{
  dir_t d = 0;
  if (top) {
    d = ((y - hpx + hp_A < -the_spliter) ? (-1) : 1);
  } else if (btm) {
    d = ((y - hpx + hp_V > the_spliter) ? (1) : -1);
  }
  return d;
}

LINE_REGION
Lines::region(const price_t hpx) noexcept
{
  LINE_REGION area = LINE_REGION::UNKNOW;
  if (nup > 0 && ndn > 0) {
    if (hpx >= hp_dn_1 && hpx <= hp_up_1) {
      area = LINE_REGION::MIDDLE;
    } else if (hpx > hp_up_1) {
      if (nup > 1) {
        if (hpx > hp_up_2)
          area = LINE_REGION::ABOVE_2;
        else
          area = LINE_REGION::ABOVE_1;
      } else {
        area = LINE_REGION::ABOVE_2_1;
      }
    } else if (hpx < hp_dn_1) {
      if (ndn > 1) {
        if (hpx < hp_dn_2)
          area = LINE_REGION::BELOW_2;
        else
          area = LINE_REGION::BELOW_1;
      } else {
        area = LINE_REGION::BELOW_2_1;
      }
    }
  }

  return area;
}

int8_t
Lines::update_lines(const Lines& l, const dir_t d, const price_t hpx) noexcept
{
  auto update = [&](const dir_t dir) {
    int8_t updated = 0;
    if (dir > 0) {

      //
      // update line-1
      //

      if (nup > 0 && l.nup > 0) {
        if (hp_up_1 > l.hp_up_1) {
          if (l.hp_up_1 - hp_dn_1 > MINIMAL_GOOD_SPACE) {
            hp_up_1 = l.hp_up_1;
            updated = 1;
          }
        }
      }

      //
      // update line-2
      //
      if (nup == 1 && l.nup > 1) {
        if (l.hp_up_2 > hp_up_1) {
          //
          // add the line-up-2
          //
          nup = 2;
          hp_up_2 = l.hp_up_2;
          updated = 3;
        }
      } else if (nup > 1 && l.nup > 1) {
        if ((hp_up_2 > l.hp_up_2) && (l.hp_up_2 > hp_up_1)) {
          hp_up_2 = l.hp_up_2;
          updated = 2;
        }
      }

    } else {

      if (ndn > 0 && l.ndn > 0) {
        if ((l.hp_dn_1 > hp_dn_1) &&
            (hp_up_1 - l.hp_dn_1 > MINIMAL_GOOD_SPACE)) {
          hp_dn_1 = l.hp_dn_1;
          updated = 1;
        }
      }

      if (ndn == 1 && l.ndn > 1) {
        if (hp_dn_1 > l.hp_dn_2) {
          //
          // add the line-dn-2
          //
          ndn = 2;
          hp_dn_2 = l.hp_dn_2;
          updated = 3;
        }
      } else if (ndn > 1 && l.ndn > 1) {
        if ((l.hp_dn_2 > hp_dn_2) && (l.hp_dn_2 < hp_dn_1)) {
          hp_dn_2 = l.hp_dn_2;
          updated = 2;
        }
      }
    }

    return updated;
  };

  int8_t updated = 0;
  if (!d) {
    updated = update(1);
    updated += update(-1);
  } else {
    updated = update(d);
  }

  return updated;
}

void
Lines::print_hited(const price_t hp_now, const int64_t idx) noexcept
{
  printf("LRChd ");
  if (reached & LIMIT_REACHED::REACHED_UP_1) {
    printf("up_1 ");
  }
  if (reached & LIMIT_REACHED::REACHED_UP_2) {
    printf("up_2 ");
  }
  if (reached & LIMIT_REACHED::REACHED_DN_1) {
    printf("dn_1 ");
  }
  if (reached & LIMIT_REACHED::REACHED_DN_2) {
    printf("dn_2 ");
  }

  printf(" >> ");
  if (nup > 1) {
    if (!(reached & LIMIT_REACHED::REACHED_UP_2)) {
      printf("no rchd Up2, ");
    }
    if (hp_now - hp_up_2 < 0) {
      printf("Below Up2. ");
    }
  } else {
    printf("No up2 ");
  }

  if (ndn > 1) {
    if (!(reached & LIMIT_REACHED::REACHED_DN_2)) {
      printf("no rchd Dn2, ");
    }
    if (hp_now - hp_dn_2 > 0) {
      printf("Above Dn2. ");
    }

  } else {
    printf("No dn2 ");
  }
  printf("\n");
}

uint8_t
Lines::retrive_reached(const dir_t d) noexcept
{
  uint8_t r = 0;
  if (d > 0) {
    r = (reached & (LIMIT_REACHED::REACHED_UP_1 | LIMIT_REACHED::REACHED_UP_2));
  } else if (d < 0) {
    r = (reached & (LIMIT_REACHED::REACHED_DN_1 | LIMIT_REACHED::REACHED_DN_2));
  }
  return r;
}

uint8_t
Lines::retrive_opposite_reached(const dir_t d) noexcept
{
  return retrive_reached(-d);
}

uint8_t
Lines::retrive_all_reached() noexcept
{
  return reached;
}

bool
Lines::reached_opposite_line1(const dir_t d) noexcept
{
  return (
    (d > 0 &&
     (reached & (LIMIT_REACHED::REACHED_DN_1 | LIMIT_REACHED::REACHED_DN_2))) ||
    (d < 0 &&
     (reached & (LIMIT_REACHED::REACHED_UP_1 | LIMIT_REACHED::REACHED_UP_2))));
}

bool
Lines::reached_opposite_line2(const dir_t d) noexcept
{
  return ((d > 0 && (reached & LIMIT_REACHED::REACHED_DN_2)) ||
          (d < 0 && (reached & LIMIT_REACHED::REACHED_UP_2)));
}

bool
Lines::reached_line1(const dir_t d) noexcept
{
  return (
    (d < 0 &&
     (reached & (LIMIT_REACHED::REACHED_DN_1 | LIMIT_REACHED::REACHED_DN_2))) ||
    (d > 0 &&
     (reached & (LIMIT_REACHED::REACHED_UP_1 | LIMIT_REACHED::REACHED_UP_2))));
}
bool
Lines::reached_line2(const dir_t d) noexcept
{
  return ((d < 0 && (reached & LIMIT_REACHED::REACHED_DN_2)) ||
          (d > 0 && (reached & LIMIT_REACHED::REACHED_UP_2)));
}

bool
Lines::almost_reached_line2(const dir_t d) const noexcept
{
  return ((d < 0 && ndn > 1 && nd.dn_2_pct < 0.14) ||
          (d > 0 && nup > 1 && nd.up_2_pct < 0.14));
}

bool
Lines::only_one_line(const dir_t d) const noexcept
{
  return ((d < 0 && ndn == 1) || (d > 0 && nup == 1));
}

#define MIN_LINE_LARGE_SPACE 35

bool
Lines::only_one_line_and_large_space(const dir_t d,
                                     const price_t hpx) const noexcept
{
  return ((d > 0 && nup == 1 && (hpx - hp_up_1 > MIN_LINE_LARGE_SPACE)) ||
          (d < 0 && ndn == 1 && (hp_dn_1 - hpx > MIN_LINE_LARGE_SPACE)));
}

dir_t
Lines::get_ext_hit_dir(const ExtRegion& rgn) noexcept
{
  uint8_t rchd_up = 0;
  uint8_t rchd_dn = 0;
  price_t A = rgn.get_top();
  price_t V = rgn.get_btm();
  if (nup == 1) {
    if (A > hp_up_1)
      rchd_up |= LIMIT_REACHED::REACHED_UP_2;
  }
  if (nup == 2) {
    if (A > hp_up_1)
      rchd_up |= LIMIT_REACHED::REACHED_UP_1;
    if (A > hp_up_2)
      rchd_up |= LIMIT_REACHED::REACHED_UP_2;
  }
  if (ndn == 1) {
    if (V < hp_dn_1)
      rchd_dn |= LIMIT_REACHED::REACHED_DN_2;
  }
  if (ndn == 2) {
    if (V < hp_dn_1)
      rchd_dn |= LIMIT_REACHED::REACHED_DN_1;
    if (V < hp_dn_2)
      rchd_dn |= LIMIT_REACHED::REACHED_DN_2;
  }

  dir_t d = 0;
  if (rchd_up && rchd_dn) {
    if (rgn.top && !rgn.btm)
      d = -1;
    else if (!rgn.top && rgn.btm)
      d = 1;
  } else if (rchd_up) {
    d = -1;
  } else if (rchd_dn) {
    d = 1;
  }
  return d;
}

price_t
Lines::get_golden_point(const dir_t d) const noexcept
{
  if (d > 0) {
    if (ndn && nup) {
      auto bottom = get_line2_price(-1);
      return (hp_up_1 - bottom) * phi + bottom;
    }
  } else if (d < 0) {
    if (ndn && nup) {
      auto top = get_line2_price(1);
      return (top - hp_dn_1) * ppi + hp_dn_1;
    }
  }
  return (0);
}

uint8_t
Lines::ext_reached() noexcept
{
  uint8_t rchd_up = 0;
  uint8_t rchd_dn = 0;
  price_t A = ext_rgn.get_top();
  price_t V = ext_rgn.get_btm();
  if (nup == 1) {
    if (A > hp_up_1)
      rchd_up |= LIMIT_REACHED::REACHED_UP_2;
  }
  if (nup == 2) {
    if (A > hp_up_1)
      rchd_up |= LIMIT_REACHED::REACHED_UP_1;
    if (A > hp_up_2)
      rchd_up |= LIMIT_REACHED::REACHED_UP_2;
  }
  if (ndn == 1) {
    if (V < hp_dn_1)
      rchd_dn |= LIMIT_REACHED::REACHED_DN_2;
  }
  if (ndn == 2) {
    if (V < hp_dn_1)
      rchd_dn |= LIMIT_REACHED::REACHED_DN_1;
    if (V < hp_dn_2)
      rchd_dn |= LIMIT_REACHED::REACHED_DN_2;
  }

  uint8_t rchd = 0;
  if (rchd_up && rchd_dn) {
    if (ext_rgn.top && !ext_rgn.btm)
      rchd = rchd_up;
    else if (!ext_rgn.top && ext_rgn.btm)
      rchd = rchd_dn;
  } else if (rchd_up) {
    rchd = rchd_up;
  } else if (rchd_dn) {
    rchd = rchd_dn;
  }

  return rchd;
}

bool
limit_reached_same_side(const dir_t d, const uint8_t reached)
{
  return (d > 0) ? (reached &
                    (LIMIT_REACHED::REACHED_UP_1 | LIMIT_REACHED::REACHED_UP_2))
                 : (reached & (LIMIT_REACHED::REACHED_DN_1 |
                               LIMIT_REACHED::REACHED_DN_2));
}

bool
limit_reached_line2(const dir_t d, const uint8_t reached)
{
  return ((d > 0 && reached & LIMIT_REACHED::REACHED_UP_2) ||
          (d < 0 && reached & LIMIT_REACHED::REACHED_DN_2));
}

bool
limit_reached_line1(const dir_t d, const uint8_t reached)
{
  return ((d > 0 && reached & LIMIT_REACHED::REACHED_UP_1) ||
          (d < 0 && reached & LIMIT_REACHED::REACHED_DN_1));
}

uint8_t
idx_of_reached_line(const uint8_t reached)
{
  return (reached & (LIMIT_REACHED::REACHED_DN_1 | LIMIT_REACHED::REACHED_UP_1))
           ? uint8_t(1)
           : ((reached &
               (LIMIT_REACHED::REACHED_DN_2 | LIMIT_REACHED::REACHED_UP_2))
                ? uint8_t(2)
                : uint8_t(0));
}

void
print_reached(const char* name,
              const dir_t d,
              const uint8_t reached,
              const int64_t idx) noexcept
{
  if (reached) {
    printf("--->>>--- %s %d Reached: ", name, d);
    if (reached & LIMIT_REACHED::REACHED_DN_1) {
      printf("dn_1 ");
    }
    if (reached & LIMIT_REACHED::REACHED_DN_2) {
      printf("dn_2 ");
    }
    if (reached & LIMIT_REACHED::REACHED_UP_1) {
      printf("up_1 ");
    }
    if (reached & LIMIT_REACHED::REACHED_UP_2) {
      printf("up_2 ");
    }
    if (reached & LIMIT_REACHED::REACHED_TIMER_OUT) {
      printf("timer out ");
    }
    printf(" --- g%ld --- \n", idx);
  }
}

bool
longer_than(const time_t tm, const time_t t_now, const int64_t delta) noexcept
{
  return (tm - t_now > delta);
}

bool
less_than(const time_t tm, const time_t t_now, const int64_t delta) noexcept
{
  return (tm - t_now < delta);
}

bool
Lines::good_entry_region(const dir_t d, const price_t hpx) noexcept
{
  price_t good_price = 0;
  bool yes = false;
  if (good_main_space()) {
    price_t h = main_space() * phi;
    good_price = (d > 0) ? (hp_up_1 - h) : (hp_dn_1 + h);
    yes = ((good_price - hpx) * d > 0);
  }
  return yes;
}

bool
Lines::good_line_region(const dir_t d, const price_t hpx) noexcept
{
  price_t good_price = 0;
  bool yes = false;
  if (good_main_space()) {
    const price_t h = 5;
    good_price = (d > 0) ? (hp_up_1 - h) : (hp_dn_1 + h);
    yes = ((good_price - hpx) * d > 0);
  }
  return yes;
}

price_t
Lines::get_line1_price(const dir_t d) const noexcept
{
  if (d > 0 && nup)
    return hp_up_1;
  else if (d < 0 && ndn)
    return hp_dn_1;
  else
    return (0);
}

price_t
Lines::get_line2_price(const dir_t d) const noexcept
{
  if (d > 0 && nup)
    return (nup > 1) ? hp_up_2 : hp_up_1;
  else if (d < 0 && ndn)
    return (ndn > 1) ? hp_dn_2 : hp_dn_1;
  else
    return (0);
}

void
Lines::get_golden_points(LinesGodenPnt& lgp) noexcept
{
  lgp.reset();
  if (!nup || !ndn)
    return;

  lgp.nup = nup;
  lgp.ndn = ndn;
  if (nup > 1) {
    lgp.gp_up2 = (hp_up_2 - hp_dn_1) * ppi + hp_dn_1;
  }
  lgp.gp_up1 = (hp_up_1 - hp_dn_1) * ppi + hp_dn_1;

  if (ndn > 1)
    lgp.gp_dn2 = (hp_up_1 - hp_dn_2) * phi + hp_dn_2;

  lgp.gp_dn1 = (hp_up_1 - hp_dn_1) * phi + hp_dn_1;
}

void
Lines::clear_reached_sapce() noexcept
{
  reached = 0;
}

price_t
Lines::get_dn_price() const noexcept
{
  if (ndn == 1)
    return hp_dn_1;
  else if (ndn > 1)
    return hp_dn_2;
  return 0;
}

price_t
Lines::get_up_price() const noexcept
{
  if (nup == 1)
    return hp_up_1;
  else if (nup > 1)
    return hp_up_2;
  return 0;
}

int
Lines::have_space_to_renew(const Lines& nl) const noexcept
{
  dir_t dn = 0, du = 0;
  if (nl.get_dn_price() < get_dn_price() - Lines::MINIMAL_LINE_CHANGE -
                            Lines::MINIMAL_LINE_CHANGE) {
    dn = -1;
  }

  if (nl.get_up_price() >
      get_up_price() + Lines::MINIMAL_LINE_CHANGE + Lines::MINIMAL_GOOD_SPACE) {
    du = 1;
  }

  int ret = 0;
  if (dn && du)
    ret = 3; // -1 and 1
  else if (dn)
    ret = 2; // -1
  else if (du)
    ret = 1; // 1

  return ret;
}

void
MaxMin2h::new_data(const price_t hpx, const time_t tm) noexcept
{
  PriceTime pt(hpx, tm);
  p2h.new_data(pt);
  mm.hp_max = -1e20;
  mm.hp_min = 1e20;
  for (ca_iter it = p2h.it_front(); it != ca_iter_end; it = p2h.it_next_(it)) {
    auto p = p2h.get_data(it);
    if (p->hp > mm.hp_max) {
      mm.hp_max = p->hp;
      mm.tm_max = p->tm;
    }

    if (p->hp < mm.hp_min) {
      mm.hp_min = p->hp;
      mm.tm_min = p->tm;
    }
  }
}

void
convert_line_set(const UpLineSet& ups,
                 const DnLineSet& dns,
                 PredictLine10& up10,
                 PredictLine10& dn10) noexcept
{
  up10.size = ups.size() > 10 ? 10 : ups.size();
  usize_t i = 0;
  for (auto it = ups.begin(); i < up10.size && it != ups.end(); ++it, ++i) {
    up10.pla[i].hpx = (*it)->hpx;
    up10.pla[i].plt = (*it)->plt;
    up10.pla[i].reached = (*it)->reached;
    up10.pla[i].ts = (*it)->ts;
  }

  dn10.size = dns.size() > 10 ? 10 : dns.size();
  i = 0;
  for (auto it = dns.begin(); i < dn10.size && it != dns.end(); ++it, ++i) {
    dn10.pla[i].hpx = (*it)->hpx;
    dn10.pla[i].plt = (*it)->plt;
    dn10.pla[i].reached = (*it)->reached;
    dn10.pla[i].ts = (*it)->ts;
  }
}

} // namespace Trade
